# -*- coding: utf-8 -*-  
import sys
import os
import xlrd
# xlrd 1.2.0后面得版本取消了对xlsx文件得读取，因此安装命令应为：pip install xlrd==1.2.0
from XlsxHelper import XlsxHelper
from tqdm import tqdm
# xlwings，比xlsxwriter快10倍，但是需要win32api，教程：https://zhuanlan.zhihu.com/p/54003662

class xlsxmerge:
    '''Xlsx文件合并工具'''
    __default_config = {
                "inputFileDir":"testData\\",
                "file_extension":".xls",
                "outputFileName":"testData",
                "validTitleRowTag":"序号",
                "copyDataRowEndTag":"页码：",
                "isInsertTitleRow":True
            }

    def __init__(self,skip_input_config:bool=False,config:list=None):
        '''创建合并工具实例（是否直接使用配置文件执行，配置文件)'''
        self.skip_input_config = skip_input_config
        if config is None:
            self.config = []
        else:
            self.config = config
            self.__default_config = config[0]
    def replaceStrInputArr(self,input_value:str):
        if(all([input_value.count("，")==0,input_value.count("\t")>0])):
            input_value = input_value.replace("\t", "，")
        return input_value

    def setting_menu(self):
        '''输入配置信息'''
        print("在执行Xlsx文件合并之前需要做以下配置，使用默认值请直接回车。")
        while True:
            inputFileDir=self.__input("inputFileDir","被合并的文件所在目录")
            file_extension=self.__input("file_extension","将要被合并文件的扩展名")
            outputFileName=self.__input("outputFileName","输出文件的文件名")
            validTitleRowTag=self.__input("validTitleRowTag","标题行特有的值，用中文“，”分隔")
            validTitleRowTag = self.replaceStrInputArr(validTitleRowTag)

            copyDataRowEndTag=self.__input("copyDataRowEndTag",'数据行结束标志,“@None”表示没有数据，用中文“，”分隔"')
            copyDataRowEndTag = self.replaceStrInputArr(copyDataRowEndTag)


            isInsertTitleRow=True

            config_item = {
                "inputFileDir":inputFileDir,
                "file_extension":file_extension,
                "outputFileName":outputFileName,
                "validTitleRowTag":validTitleRowTag,
                "copyDataRowEndTag" :copyDataRowEndTag,
                "isInsertTitleRow":isInsertTitleRow
            }
            self.config.append(config_item)
            loop = input("是否继续合并另一个目录？[Y:是，其他:否]")
            if loop in ["Y","y"]:
                print("请输入下一个要合并文件的配置：")
            else:
                break
        # end while
            
    def __input(self,config_key:str,input_title:str):
        '''显示输入配置的操作(配置的键，显示的提示)'''
        originalValue = self.__default_config[config_key]
        inputText = input(f"请输入，{input_title}（默认值：{originalValue}）：")
        newVal = inputText if inputText!="" else originalValue
        return newVal

    def get_dir_files(self,file_dir,file_extension):   
        '''获取指定目录下所有文件(目录路径，搜索文件的扩展名)'''
        L=[]   
        for root, dirs, files in os.walk(file_dir): 
            for file in files:  
                if file.endswith(file_extension):
                    filename = os.path.join(root, file)
                    L.append(filename)  
        return L 

    def merge(self):
        '''执行合并'''
        if self.skip_input_config == False:
            self.setting_menu()

        last_dir = ""
        last_save_file = ""
        xlsx_helper = XlsxHelper()
        # 循环合并所有配置
        for config in self.config:
            # 被合并的文件所在目录,标题行关键字,有效数据行关键字,是否插入第一个标题行,输出到sheet的名称,单个文件的后缀名(包括点)
            file_dir = config["inputFileDir"] 
            file_extension = config["file_extension"]
            outputSheetName = config["outputFileName"]
            outputSheetName = outputSheetName if outputSheetName.endswith(file_extension) else outputSheetName+file_extension
            print(f"\n开始对：{file_dir} 中的扩展名为“{file_extension}”的文件，进行合并操作...")
            # 目录不存在，创建目录
            if os.path.isdir(file_dir)==False:
                os.mkdir(file_dir)
                print(f"已生成 {file_dir} 目录，请将要合并成 {outputSheetName} 的Xlsx文件放进该目录。")
                continue

            isInsertedTitleRow = False
            '''是否已经插入了标题行'''
            # 判定是否标题行
            check_title_row = self.make_assert_row_values_expression(config["validTitleRowTag"],"，")
            # 判定是否不是数据行
            check_not_data_row = self.make_assert_row_values_expression(config["copyDataRowEndTag"]) 

            sheetData = []
            # 被合并的目录
            input_files = self.get_dir_files(file_dir,file_extension)
            fileCount = len(input_files)
            if fileCount== 0:
                print(f"合并数据目录 {file_dir} 里没有“{file_extension}”文件，请将需要合并的文件放进该目录。")
                continue
            # 读取数据
            print(f"开始读取“{file_dir}”数据...共{fileCount}个文件：")
            for i in tqdm(range(fileCount)):  
                filename = input_files[i]  
                try:
                    wb = xlrd.open_workbook(filename)
                except PermissionError:
                    print(f"文件{filename}正在被使用，请关闭文件后重新启动本程序。")
                    return
                except Exception as ex:
                    if ex == "Excel xlsx file; not supported":
                        print(f"文件{filename}读取失败，原因：xlrd版本不兼容，请执行下面的命令进行降级：\n\n pip install xlrd==1.2.0")
                    else:
                        print(f"文件{filename}读取失败，错误信息：{ex}")
                    return
                # 读取工作表
                sheet = wb.sheets()[0]
                # 是否开始读取数据
                isReadData = False
                # 遍历读取每一行到数据缓存对象
                for rownum in range(sheet.nrows):
                    rowdata = sheet.row_values(rownum)
                    # 处于读取数据状态，
                    if isReadData:
                        # 不包含数据结束行标志
                        if check_not_data_row(rowdata):                            
                            # 将读取状态挂起
                            isReadData = False
                        else:
                            # 插入数据
                            self.append_data_row(sheetData,rowdata,filename)

                    # 非读取状态下，判定是否是标题行
                    elif check_title_row(rowdata):
                        # 是标题行，恢复读取状态
                        isReadData = True
                        # 如果没有插入了标题行
                        if isInsertedTitleRow == False:
                            sheetData.append(rowdata)
                            self.append_data_row(sheetData,rowdata,"数据来源")
                            # 指示程序已经插入过标题行了
                            isInsertedTitleRow = True
  
            # 将合并后的数据写入文件
            xlsx_helper.save_to_new_file(outputSheetName,sheetData)
    def append_data_row(self,sheetData,rowdata,filename):
        """在插入数据时，附加额外的列数据"""
        rowdata.append(filename)
        sheetData.append(rowdata)

    def make_assert_row_values_expression(self,expression:str,split_char:str=","):
        '''生成判定数据是否符合的断言闭包(从左到右的数值，数值分隔符)'''
        expArr = []
        for exp in expression.split(split_char):
            if exp=="@None":
                expArr.append(None)
            else:
                expArr.append(exp)
        def assert_value(row_values):
            '''判定行数据是否符合设置'''
            return all(expArr[i]==row_values[i] for i in range(len(expArr)))
        # 返回闭包
        return assert_value

if __name__=="__main__":
    '''测试xlsx数据合并'''
    # 无参
    if len(sys.argv)==1:
        xm = xlsxmerge()
        xm.merge()
    elif all([len(sys.argv)==2,sys.argv[1]=="--help"]):
        print(r'''
合并工具：xlsxmerge.py
命令行参数：
py xlsxmerge.py [--help][-i .\xlsfolder\][-f .xls][-o outputFileName][-t 序号，装货单位][-e 页码：]

--help              显示当前帮助
-i .\xlsfolder\     输入将要被合并文件的目录
-f .xls             将要被合并文件的扩展名，仅支持“.xls”和“.xlsx”
-o outputFileName   输出合并后的文件名，无须填写扩展名
-t 序号，装货单位   标题行的前若干个单元格值，用中文逗号“，”分隔
-e 页码：           指示改行不再是数据行的前若干个单元格值，用中文逗号“，”分隔

        ''')
    else:
        config = {}
        args = {
            "-i":"inputFileDir",
            "-f":"file_extension",
            "-o":"outputFileName",
            "-t":"validTitleRowTag",
            "-e":"copyDataRowEndTag"
        }
        for i in range(1,len(sys.argv)-1):
            if sys.argv[i] in args.keys():
                config[args[sys.argv[i]]] = sys.argv[i+1]
                continue
            
        config["isInsertTitleRow"] = True
        xm = xlsxmerge(True,[config])
        xm.merge()
